
#include "Jeu.h"

#include <stdio.h>
#include <stdlib.h>

/* à améliorer : initialiser le jeu à partir des données d'un fichier de sauvegarde... */
void jeuInit(Jeu* p_jeu)
{
	carInit(&p_jeu->carte);

    linInit(&p_jeu->link);

	p_jeu->tab_monstres = (Monstre*) malloc(15 * sizeof (Monstre));

	carChangeZone(&p_jeu->carte, 0); /* pour l'instant on commence à la zone 0 forcément, plus tard autre chose...avec la sauvegarde... */
    jeuChargeMonstresZone(p_jeu, 0);
}


void jeuLibere(Jeu* p_jeu)
{
	free(p_jeu->tab_monstres);
}


Carte* jeuGetPCarte(Jeu* p_jeu)
{
	return &(p_jeu->carte);
}

const Carte* jeuGetConstPCarte(const Jeu* p_jeu)
{
	return &(p_jeu->carte);
}


Link* jeuGetPLink(Jeu* p_jeu)
{
    return &(p_jeu->link);
}
const Link* jeuGetConstPLink(const Jeu* p_jeu)
{
    return &(p_jeu->link);
}


Monstre* jeuGetTabMonstres(Jeu* p_jeu)
{
    return p_jeu->tab_monstres;
}
const Monstre* jeuGetConstTabMonstres(const Jeu* p_jeu)
{
    return p_jeu->tab_monstres;
}


void jeuActionClavier(Jeu* p_jeu, char touche)
{
	switch(touche)
	{
		/* haut/bas/gauche/droite ==> mouvement */
 	    case 'h' :
                linTourne(&p_jeu->link, haut);
                if (jeuEstPositionAccessible (p_jeu, linGetPosX(&p_jeu->link), linGetPosY(&p_jeu->link)-1 ))
                {
                    linDeplace(&p_jeu->link, haut);
                }
				break;
		case 'b' :
                linTourne(&p_jeu->link, bas);
                if (jeuEstPositionAccessible (p_jeu, linGetPosX(&p_jeu->link), linGetPosY(&p_jeu->link)+1 ))
                {
                    linDeplace(&p_jeu->link, bas);
                }
				break;
		case 'g' :
                linTourne(&p_jeu->link, gauche);
                if (jeuEstPositionAccessible (p_jeu, linGetPosX(&p_jeu->link)-1, linGetPosY(&p_jeu->link) ))
                {
                    linDeplace(&p_jeu->link, gauche);
                }
				break;
		case 'd' :
                linTourne(&p_jeu->link, droite);
                if (jeuEstPositionAccessible (p_jeu, linGetPosX(&p_jeu->link)+1, linGetPosY(&p_jeu->link) ))
                {
                    linDeplace(&p_jeu->link, droite);
                }
				break;

		/* attaque */
		case 'a' :
				jeuVerifieAttaqueEpee(p_jeu);
				break;
	}
}


void jeuDeplaceMonstres (Jeu* p_jeu)
{
	int i;
	Position pos_destination_temp;

	for(i=0 ; i<p_jeu->nb_monstres ; i++)
	{
		/* MAJ des états des monstres en fonction de la distance à Link */
		jeuVerifieEtatMon(p_jeu);

		/* si mode balade : déplacement aléatoire */
		if (monGetEtat(&p_jeu->tab_monstres[i]) == balade)
		{
			do
			{
				pos_destination_temp = monDeplAleatoire(&p_jeu->tab_monstres[i]);
			}
			while (jeuEstPositionAccessible (p_jeu, posGetConstX(&pos_destination_temp), posGetConstY(&pos_destination_temp) ) == false);
			monTeleporte(&p_jeu->tab_monstres[i], posGetConstX(&pos_destination_temp), posGetConstY(&pos_destination_temp)); /* à améliorer en vrai déplacement avec changement de direction */
		}
		/* si mode excité et que le monstre doit s'approcher pour toucher link (distance >=2) : déplacement d'attaque excité */
		else if ( (monGetEtat(&p_jeu->tab_monstres[i]) == excite) && (posDistance(monGetConstPosition(&p_jeu->tab_monstres[i]),linGetConstPosition(&p_jeu->link)) >= 2) )
		{
			do
			{
				pos_destination_temp = monDeplAleatoire(&p_jeu->tab_monstres[i]); /* temporaire (lire ligne suivante) */
				/* pos_destination_temp = monDeplExcite(&p_jeu->tab_monstres[i], linGetConstPosition(&p_jeu->link));  *****************  BUG si plusieurs monstres suivent link en même temps, c'est pour ça qu'on a mis déplacement aléatoire pour l'instant*****************/
			}
			while (jeuEstPositionAccessible (p_jeu, posGetConstX(&pos_destination_temp), posGetConstY(&pos_destination_temp) ) == false);
			monTeleporte(&p_jeu->tab_monstres[i], posGetConstX(&pos_destination_temp), posGetConstY(&pos_destination_temp));
		}
	}
}

bool jeuEstPositionAccessible (const Jeu* p_jeu, int x, int y)
{
    int i;
    bool resultat;

    /* on vérifie que la postion de la zone est de type accessible  et  on vérifie que link n'est pas sur cet emplacement */
    resultat =  zonEstPositionAccessible( carGetConstPZoneActive(&p_jeu->carte), x, y) &&
                ( x != linGetPosX(&p_jeu->link) || y != linGetPosY(&p_jeu->link) );


    /* si c'est bon, on vérifie qu'il n'y a pas de monstre vivant à cet emplacement */
    i=0;
    while ( i<p_jeu->nb_monstres && ( monGetEtat(&p_jeu->tab_monstres[i])==mort || (x!=monGetPosX(&p_jeu->tab_monstres[i]) || y!=monGetPosY(&p_jeu->tab_monstres[i])) ) )
    {
        i++;
    }

    if (i<p_jeu->nb_monstres) /* si  i < nb_monstres c'est que l'un des monstres occupe la case désirée */
    {
        resultat = false;
    }

    return resultat;

}



void jeuVerifieEvenements(Jeu* p_jeu)
{
	jeuVerifieObjets(p_jeu);

	jeuVerifieChangementZone(p_jeu);
}



void jeuVerifieObjets(Jeu* p_jeu)
{
	/* si Link marche sur un coeur, il le récupère et le coeur disparaît */
	if (zonGetObjetXY(carGetConstPZoneActive(&p_jeu->carte), linGetPosX(&p_jeu->link), linGetPosY(&p_jeu->link)) == coeur)
	{
		linChangeNbCoeurs(&p_jeu->link, +1);
		bloSetObjet(zonGetPBlocObjetXY(carGetPZoneActive(&p_jeu->carte),linGetPosX(&p_jeu->link), linGetPosY(&p_jeu->link)), aucun);
	}
}



void jeuVerifieMonstreAttaqueContact(Jeu* p_jeu, clock_t* p_date_derniere_attaque)
{
	int i;
	clock_t date = clock();

	/* Si la dernière attaque a eu lieu moins de 75 centièmes de secondes auparavant, pas de nouvelle attaque, ça limite la difficulté mais en fait bof, ça permet pas de se prendre des attaques de plusieurs monstres en même temps... */
	if ((date - *p_date_derniere_attaque) > CLOCKS_PER_SEC * 75/100)
	{
		for(i=0 ; i<p_jeu->nb_monstres ; i++)
		{
			Monstre* p_monstre = &p_jeu->tab_monstres[i];

			/* un monstre va attaquer link s'il est sur un bloc adjacent à celui de Link, c'est à dire que la distance entre eux est inférieure stricte à 2 (prend en compte les cas en diagonales) */
			if ( (monGetEtat(p_monstre) != mort) && (posDistance( monGetConstPosition(p_monstre), linGetConstPosition(&p_jeu->link) ) < 2) )
			{
				linChangeNbCoeurs(&p_jeu->link, -1);
			}
		}

		*p_date_derniere_attaque = date;
	}


}


void jeuVerifieAttaqueEpee(Jeu* p_jeu)
{
	int i,j;
    Position tab_de_positions_touchees[3];

    linAttaqueEpee(&p_jeu->link, tab_de_positions_touchees);

	for(j=0 ; j<p_jeu->nb_monstres ; j++)
    {
        i=0;
		while( (posEgales(&tab_de_positions_touchees[i], monGetConstPosition(&p_jeu->tab_monstres[j])) == false) && i<3 )
        {
			i++;
        }

        if (i != 3)
        {
        	if (monGetEtat(&p_jeu->tab_monstres[j]) != mort)
        	{
				monChangeNbCoeurs(&p_jeu->tab_monstres[j], -1);
				/* s'il est mort, on fait apparaître un coeur à sa place */
				if (monGetEtat(&p_jeu->tab_monstres[j]) == mort)
				{
					bloSetObjet(zonGetPBlocObjetXY(carGetPZoneActive(&p_jeu->carte), monGetPosX(&p_jeu->tab_monstres[j]), monGetPosY(&p_jeu->tab_monstres[j])), coeur);
				}
        	}
        }
    }
}



void jeuVerifieEtatMon(Jeu* p_jeu)
{
	Position* p_pos_link;
	Position* p_pos_monstre;
	int i;

	p_pos_link = linGetConstPosition(&p_jeu->link);

    for(i=0;i<(p_jeu->nb_monstres);i++)
    {
        p_pos_monstre = monGetConstPosition(&p_jeu->tab_monstres[i]);

        if (monGetEtat(&p_jeu->tab_monstres[i]) != mort)
        {
        	if (posDistance(p_pos_link, p_pos_monstre) <6 )
        	{
        		monSetEtat(&p_jeu->tab_monstres[i], excite);
        	}
        	else
        	{
        		monSetEtat(&p_jeu->tab_monstres[i], balade);
        	}
        }

    }
}



void jeuVerifieChangementZone(Jeu* p_jeu)
{
    int x = linGetPosX(&p_jeu->link);
    int y = linGetPosY(&p_jeu->link);
    int num_nouvelle_zone;

    if (zonEstChangeurZone( carGetConstPZoneActive(&p_jeu->carte), x, y) )
    {
        /* Changement de zone et */
    	/* Si link est sur un changeur de zone qui se trouve sur l'un des bords de la zone,*/
		/* il faut changer sa position pour qu'il atterisse sur le bon bord de la nouvelle zone */

    	if (x == 0)   /* bord ouest -> bord est */
    	{
    		num_nouvelle_zone = zonGetNumZoneOuest(carGetPZoneActive(&p_jeu->carte));
    		carChangeZone(&p_jeu->carte, num_nouvelle_zone);

            x = zonGetDimension(carGetPZoneActive(&p_jeu->carte)) - 2;
            linTeleporte(&p_jeu->link, x, y);

    		jeuChargeMonstresZone(p_jeu, num_nouvelle_zone);


		}
    	else if (y == 0)  /* bord nord -> bord sud */
    	{
            num_nouvelle_zone = zonGetNumZoneNord(carGetPZoneActive(&p_jeu->carte));
    		carChangeZone(&p_jeu->carte, num_nouvelle_zone);

            y = zonGetDimension(carGetPZoneActive(&p_jeu->carte)) - 2;
            linTeleporte(&p_jeu->link, x, y);

            jeuChargeMonstresZone(p_jeu, num_nouvelle_zone);
    	}
    	else if (x == zonGetDimension(p_jeu->carte.p_zone_active)-1 )  /* bord est -> bord ouest */
    	{
            num_nouvelle_zone = zonGetNumZoneEst(carGetPZoneActive(&p_jeu->carte));
    		carChangeZone(&p_jeu->carte, num_nouvelle_zone);

            x = 1;
            linTeleporte(&p_jeu->link, x, y);

            jeuChargeMonstresZone(p_jeu, num_nouvelle_zone);
    	}
    	else if (y == zonGetDimension(p_jeu->carte.p_zone_active)-1 )  /* bord sud -> bord nord */
    	{
            num_nouvelle_zone = zonGetNumZoneSud(carGetPZoneActive(&p_jeu->carte));
    		carChangeZone(&p_jeu->carte, num_nouvelle_zone);

            y = 1;
            linTeleporte(&p_jeu->link, x, y);

            jeuChargeMonstresZone(p_jeu, num_nouvelle_zone);
    	}
    }

    /* si la nouvelle position n'est pas accessible : pb !*/
	if ( zonEstPositionAccessible(p_jeu->carte.p_zone_active, x, y) == false )
	{
		fprintf(stderr, "La nouvelle position n'est pas accessible ! Problème de conception de la carte ! \n");
	}
}


void jeuChargeMonstresZone(Jeu* p_jeu, int num_zone)
{
	int i;
	int x,y;
	int dim_zone;
	int nb_monstres_alea;
	Categorie categorie;
	Position pos_temp;

	/* on met à jour le tableau de monstres */
	/* avec les monstres dont le placement est aléatoire : */
	nb_monstres_alea = zonGetNbMonstresAleatoires(carGetConstPZoneActive(&p_jeu->carte));
	dim_zone = zonGetDimension(carGetConstPZoneActive(&p_jeu->carte));

	for(i=0 ; i<nb_monstres_alea ; i++)
	{
		categorie = soldat; /* il faudrait pouvoir choisir la categorie... */

		/* on choisit une position aléatoire pour le monstre en veillant à ce que la position soit accessible et que ce soit un petit peu éloigné de Link */
		do
		{
			x = rand() % dim_zone ;
			y = rand() % dim_zone ;

			posSetX(&pos_temp, x);
			posSetY(&pos_temp, y);
		}
		while( (jeuEstPositionAccessible(p_jeu, x, y) == false) || (posDistance(linGetConstPosition(&p_jeu->link), &pos_temp) < 5) );

		monInit(&p_jeu->tab_monstres[i], categorie, x, y);
	}
	p_jeu->nb_monstres = nb_monstres_alea;
}


bool jeuLinkEstMort(const Jeu* p_jeu)
{
	if (linGetNbVies(&p_jeu->link) <= 0)
	{
		return true;
	}
	else
	{
		return false;
	}
}


bool jeuTestRegression(Jeu* p_jeu)
{
    /* à faire... */
}
